﻿#region Copyright and Licensing Notice
// Copyright (C) 2011 Steven Bosnick
// 
// This program is open source software; you can redistribute it 
// and/or modifiy it under the terms of the Eclipse Public License,
// version 1.0.
// 
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A 
// PARTICULAR PURPOSE. See the Eclipse Public License for more
// details.
// 
// You should have received a copy of the Eclipse Public License 
// along with this program; if not, you can find a copy at
// "http://www.opensource.org/licenses/eclipse-1.0.php".
#endregion
using System;
using System.Threading;
using System.Threading.Tasks;
using BitwiseOre.SharedMemory.Core.Common;
using NUnit.Framework;

namespace BitwiseOre.Tests.SharedMemory.Common
{
    [TestFixture]
    public sealed class TaskExtensionsFixture : AssertionHelper
    {
        [Test(Description = "Checks that the Then continuation task uses the supplied action on a normal completion")]
        public void Then_OnNormalCompletion()
        {
            var initialtask = Task.Factory.StartNew(() => { });
            var actioncalled = false;

            var then = initialtask.Then(() =>
                                            {
                                                actioncalled = true;
                                                return true;
                                            });
            then.Wait();

            Expect(actioncalled, EqualTo(true),
                   "Completion action was not called.");
        }

        [Test(Description = "Checks that the Then continuation propegates the exceptions from the prior task")]
        public void Then_OnFault()
        {
            var aethrown = false;
            var exception = new TimeoutException();
            var initialtask = Task.Factory.StartNew(() => { throw exception; });
            var then = initialtask.Then(() => true);

            try
            {
                then.Wait();
            }
            catch (AggregateException e)
            {
                aethrown = true;
                Expect(e.Flatten().InnerExceptions, Contains(exception));
            }

            Expect(aethrown, "Expected AggregateException was not thrown.");
        }

        [Test(Description = "Checks that the Then continuation does not used the supplied action for a cancellation")]
        public void Then_OnCancelled()
        {
            var source = new CancellationTokenSource();
            var initialtask = Task.Factory.StartNew(
                () =>
                    {
                        while (true)
                        {
                            source.Token.ThrowIfCancellationRequested();
                        }
                    }, source.Token);
            var actioncalled = false;

            var then = initialtask.Then(() =>
                                            {
                                                actioncalled = true;
                                                return true;
                                            });
            source.Cancel();

            try
            {
                then.Wait();
            }
            catch (AggregateException)
            {
            }

            Expect(actioncalled, EqualTo(false),
                   "Continuation action called after initial task cancelled.");
        }

        [Test(Description = "The last result of an enumeration of tasks is the result of the last task.")]
        public void LastResult_ReturnsResultOfLastTask()
        {
            const int lastresult = 5;
            Task<int>[] tasks = {
                                    Task.Factory.StartNew(() => 3),
                                    Task.Factory.StartNew(() => 2),
                                    Task.Factory.StartNew(() => 1),
                                    Task.Factory.StartNew(() => lastresult)
                                };

            var result = tasks.LastResult();

            Expect(result.Result, EqualTo(lastresult), "Unexpected result returned from as the last result.");
        }

        [Test(Description = "The last result of an enumberation of tasks propagates faults from the first " +
                            "task in which they occur.")]
        public void LastResult_PropegatesFaultsEarly()
        {
            Exception[] exceptions = {new Exception(), new Exception() };
            Func<int>[] throwers = {
                                       delegate { throw exceptions[0]; },
                                       delegate { throw exceptions[1]; }
                                   };
            Task<int>[] tasks = {
                                    Task.Factory.StartNew(() => 3),
                                    Task.Factory.StartNew(throwers[0]),
                                    Task.Factory.StartNew(() => 1),
                                    Task.Factory.StartNew(throwers[1])
                                };


            try
            {
                Task.WaitAll(tasks);
            }
            catch (Exception)
            {
            }

            var result = tasks.LastResult();
            try
            {
                result.Wait();
            }
            catch (Exception)
            {
            }

            Expect(result.Exception.InnerExceptions, Contains(exceptions[0]),
                   "Expected exception not propated from last result.");
            Expect(result.Exception.InnerExceptions, Not.Contains(exceptions[1]),
                   "Unexpected exception propagated from last result");
        }


    }
}